<?php

declare(strict_types=1);

namespace app\service\sys;

use InvalidArgumentException;

class ConfigTransformer
{
    public static function castValue(mixed $value, string $type): mixed
    {
        switch ($type) {
            case 'json':
                if (is_array($value)) {
                    return $value;
                }

                if ($value === null || $value === '') {
                    return [];
                }

                if (is_string($value)) {
                    $decoded = json_decode($value, true);
                    if (json_last_error() === JSON_ERROR_NONE) {
                        return $decoded;
                    }

                    return $value;
                }

                if (is_object($value)) {
                    return json_decode(json_encode($value, JSON_UNESCAPED_UNICODE), true);
                }

                return (array) $value;
            case 'bool':
                if (is_bool($value)) {
                    return $value;
                }

                if (is_numeric($value)) {
                    return (int) $value === 1;
                }

                if (is_string($value)) {
                    $normalized = strtolower(trim($value));
                    return in_array($normalized, ['1', 'true', 'yes', 'on'], true);
                }

                return (bool) $value;
            case 'int':
                if ($value === null || $value === '') {
                    return 0;
                }

                if (is_numeric($value) || is_bool($value)) {
                    return (int) $value;
                }

                return 0;
            case 'decimal':
            case 'float':
                if ($value === null || $value === '') {
                    return 0.0;
                }

                if (is_numeric($value) || is_bool($value)) {
                    return (float) $value;
                }

                return 0.0;
            case 'string':
            default:
                if ($value === null) {
                    return '';
                }

                return is_scalar($value) ? (string) $value : json_encode($value, JSON_UNESCAPED_UNICODE);
        }
    }

    public static function serializeValue(mixed $value, string $type): string
    {
        switch ($type) {
            case 'json':
                if ($value === null || $value === '') {
                    return '[]';
                }

                if (is_string($value)) {
                    $decoded = json_decode($value, true);
                    if (json_last_error() !== JSON_ERROR_NONE) {
                        throw new InvalidArgumentException('JSON 格式不正确');
                    }

                    return json_encode($decoded, JSON_UNESCAPED_UNICODE);
                }

                if (is_array($value) || is_object($value)) {
                    return json_encode($value, JSON_UNESCAPED_UNICODE);
                }

                throw new InvalidArgumentException('JSON 配置值必须是数组或对象');
            case 'bool':
                return self::normalizeBoolean($value) ? '1' : '0';
            case 'int':
                if ($value === null || $value === '') {
                    return '0';
                }

                if (!is_numeric($value) && !is_bool($value)) {
                    throw new InvalidArgumentException('配置值必须是整数');
                }

                return (string) ((int) $value);
            case 'decimal':
            case 'float':
                if ($value === null || $value === '') {
                    return '0';
                }

                if (!is_numeric($value) && !is_bool($value)) {
                    throw new InvalidArgumentException('配置值必须是数字');
                }

                return is_string($value) ? $value : (string) $value;
            case 'string':
            default:
                if ($value === null) {
                    return '';
                }

                return is_scalar($value) ? (string) $value : json_encode($value, JSON_UNESCAPED_UNICODE);
        }
    }

    private static function normalizeBoolean(mixed $value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        if (is_numeric($value)) {
            return (int) $value === 1;
        }

        if (is_string($value)) {
            $normalized = strtolower(trim($value));
            if ($normalized === '') {
                return false;
            }

            return in_array($normalized, ['1', 'true', 'yes', 'on'], true);
        }

        return (bool) $value;
    }
}
